package com.allin.http.request;

import android.content.Context;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
import android.widget.ImageView;

import com.allin.http.CacheManager;
import com.allin.http.OkHttpClientManager;
import com.allin.http.callback.ResultCallback;
import com.squareup.okhttp.Headers;
import com.squareup.okhttp.MediaType;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.RequestBody;

import java.io.File;
import java.io.IOException;
import java.util.IdentityHashMap;
import java.util.Map;

/**
 * HTTP请求
 * Created by zhy on 15/11/6.
 */
public abstract class OkHttpRequest {
    protected OkHttpClientManager mOkHttpClientManager = OkHttpClientManager.getInstance();
    protected OkHttpClient mOkHttpClient;

    protected RequestBody requestBody;
    protected Request request;

    protected String url;  // 请求URL
    protected Object tag; //请求标记 必要的时候能找到这个请求 （比如当需要取消某一个请求的时候，可以根据tag来终止请求）
    protected Map<String, Object> params; // 参数 key-value
    protected Map<String, String> headers;  //请求头部，用来定义客户端浏览器，请求页面，服务器等相关的信息

    protected OkHttpRequest(String url, Object tag,Map<String, Object> params, Map<String, String> headers) {
        mOkHttpClient = mOkHttpClientManager.getOkHttpClient(); //获取okhttp请求客户端
        this.url = url;
        this.tag = tag;
        this.params = params;
        this.headers = headers;
    }


    /**
     * 构建请求
     * @return
     */
    protected abstract Request buildRequest();

    /**
     * 请求体--保护参数以及headers的对象
     * @return
     */
    protected abstract RequestBody buildRequestBody();

    /**
     * 请求前把所有发送请求所需要的内容准备好
     *  :请求方式、参数类型、提交数据、编码格式等
     * @param callback
     */
    protected void prepareInvoked(ResultCallback callback) {
        requestBody = buildRequestBody();
        requestBody = wrapRequestBody(requestBody, callback);
        request = buildRequest();
    }


    /**
     * 异步发送请求
     * @param callback
     */
    public void invokeAsyn(ResultCallback callback) {
        prepareInvoked(callback);
        mOkHttpClientManager.execute(request, callback);
    }

    /**
     * 异步发送请求，读取缓存优先
     * @param callback
     * @param cacheAble
     * @param cacheKey
     * @param context
     */
    public void invokeAsynGet(ResultCallback callback,boolean cacheAble,boolean cacheUpdate,String cacheKey,Context context)
    {
        prepareInvoked(callback);
        if(cacheAble && !TextUtils.isEmpty(cacheKey) && context!=null){
            mOkHttpClientManager.execute(request, callback,cacheAble,cacheUpdate,cacheKey,context);
        }else{
            mOkHttpClientManager.execute(request, callback);
        }

    }

    protected RequestBody wrapRequestBody(RequestBody requestBody, final ResultCallback callback) {
        return requestBody;
    }


    public <T> T invoke(Class<T> clazz) throws IOException {
        requestBody = buildRequestBody();
        Request request = buildRequest();
        return mOkHttpClientManager.execute(request, clazz);
    }

    /**
     * 自定义头部信息
     * @param builder
     * @param headers
     */
    protected void appendHeaders(Request.Builder builder, Map<String, String> headers) {
        if (builder == null) {
            throw new IllegalArgumentException("builder can not be empty!");
        }

        Headers.Builder headerBuilder = new Headers.Builder();
        if (headers == null || headers.isEmpty()) return;

        for (String key : headers.keySet()) {
            headerBuilder.add(key, headers.get(key));
        }
        builder.headers(headerBuilder.build());
    }

    public void cancel() {
        if (tag != null)
            mOkHttpClientManager.cancelTag(tag);
    }

    /**
     * 处理相关请求，
     * 内部类中的全局属性与外部类的属性含义保持一致
     */
    public static class Builder {
        private String url;
        private Object tag;
        private boolean cacheAble;//是否需要缓存 默认存和取
        private boolean cacheUpdate=true;//是否需要强制刷新缓存
        private String cacheKey;//缓存key
        private long cacheTime;//缓存时间
        private String afterCode; //执行后返回码
        private Context context;
        private Map<String, String> headers;
        private Map<String, Object> params;
        private Pair<String, File>[] files;
        private MediaType mediaType;

        private String destFileDir;
        private String destFileName;

        private ImageView imageView;
        private int errorResId = -1;

        //for post
        private String content;
        private byte[] bytes;
        private File file;

        public Builder url(String url) {
            this.url = url;
            return this;
        }

        public Builder cache(Context context,boolean cacheAble,boolean cacheUpdate,String cacheKey,long cacheTime)
        {
            this.cacheAble = cacheAble;
            this.cacheUpdate = cacheUpdate;
            this.cacheKey = cacheKey;
            this.cacheTime=cacheTime;
            this.context=context;
            return this;
        }
        public Builder after(String afterCode)
        {
            this.afterCode = afterCode;
            return this;
        }
        public Builder tag(Object tag) {
            this.tag = tag;
            return this;
        }

        /**
         * 添加参数集合
         * @param params
         * @return
         */
        public Builder params(Map<String, Object> params) {
            this.params = params;
            return this;
        }


        public Builder bytes(byte[] bytes){
            this.bytes=bytes;
            return this;
        }

        /**
         * 添加单个参数
         * @param key
         * @param val
         * @return
         */
        public Builder addParams(String key, String val) {
            if (this.params == null) {
                params = new IdentityHashMap<>();
            }
            params.put(key, val);
            return this;
        }

        /**
         * 添加头部信息集合
         * @param headers
         * @return
         */
        public Builder headers(Map<String, String> headers) {
            this.headers = headers;
            return this;
        }

        /**
         * 添加单条头部信息
         * @param key
         * @param val
         * @return
         */
        public Builder addHeader(String key, String val) {
            if (this.headers == null) {
                headers = new IdentityHashMap<>();
            }
            headers.put(key, val);
            return this;
        }

        /**
         * 提交数据-文件
         * @param files
         * @return
         */
        public Builder files(Pair<String, File>... files) {
            this.files = files;
            return this;
        }

        /**
         * 本地存储的文件名，下载的时候可以用到
         * @param destFileName
         * @return
         */
        public Builder destFileName(String destFileName) {
            this.destFileName = destFileName;
            return this;
        }

        /**
         * 本地文件存储路径
         * @param destFileDir
         * @return
         */
        public Builder destFileDir(String destFileDir) {
            this.destFileDir = destFileDir;
            return this;
        }

        /**
         * 显示一张网络图片所用的图片控件
         * @param imageView
         * @return
         */
        public Builder imageView(ImageView imageView) {
            this.imageView = imageView;
            return this;
        }

        public Builder errResId(int errorResId) {
            this.errorResId = errorResId;
            return this;
        }

        /**
         * 提交的数据-字符串
         * @param content
         * @return
         */
        public Builder content(String content) {
            this.content = content;
            return this;
        }

        /**
         * 指定数据提交的类型/格式
         * @param mediaType
         * @return
         */
        public Builder mediaType(MediaType mediaType) {
            this.mediaType = mediaType;
            return this;
        }

        /**
         * 同步执行get请求，直接返回结果
         * @param clazz
         * @param <T>
         * @return
         * @throws IOException
         */
        public <T> T get(Class<T> clazz) throws IOException {
            OkHttpRequest request = new OkHttpGetRequest(url, tag, params, headers);
            return request.invoke(clazz);
        }

        /**
         * 异步执行get请求，callback相应接收返回数据
         * @param callback
         * @return
         */
        public OkHttpRequest get(ResultCallback callback) {
            if(cacheAble && !cacheUpdate && context!=null && CacheManager.isCacheDataValid(context,cacheKey,cacheTime)){
                Log.i("OkHttpRequest","cache get:------"+cacheKey);
                new CacheManager().new ReadCacheTask(context,callback,afterCode).execute(cacheKey);
            }else{
                OkHttpRequest request = new OkHttpGetRequest(url, tag, params, headers);
                request.invokeAsynGet(callback,cacheAble,cacheUpdate,cacheKey,context);
                return request;
            }
            return null;
        }

        /**
         * 同步 post 请求
         * @param clazz
         * @param <T>
         * @return
         * @throws IOException
         */
        public <T> T post(Class<T> clazz) throws IOException {
            OkHttpRequest request = new OkHttpPostRequest(url, tag, params, headers, mediaType, content, bytes, file);
            return request.invoke(clazz);
        }

        /**
         * 异步post请求 ，callback相应接收返回数据
         * @param callback
         * @return
         */
        public OkHttpRequest post(ResultCallback callback) {
            OkHttpRequest request = new OkHttpPostRequest(url, tag, params, headers, mediaType, content, bytes, file);
            request.invokeAsyn(callback);
            return request;
        }


        /**
         * 异步put请求，callback接收返回数据
         * @param callback
         * @return
         */
        public OkHttpRequest put(ResultCallback callback) {
            OkHttpRequest request = new OkHttpPutRequest(url, tag, params, headers, mediaType, content, bytes, file);
            request.invokeAsyn(callback);
            return request;
        }

        public OkHttpRequest upload(ResultCallback callback) {
            OkHttpRequest request = new OkHttpUploadRequest(url, tag, params, headers, files);
            request.invokeAsyn(callback);
            return request;
        }

        /**
         * 同步文件上传
         * @param clazz
         * @param <T>
         * @return
         * @throws IOException
         */
        public <T> T upload(Class<T> clazz) throws IOException {
            OkHttpRequest request = new OkHttpUploadRequest(url, tag, params, headers, files);
            return request.invoke(clazz);
        }

        /**
         * 异步文件下载
         * @return
         * @throws IOException
         */
        public OkHttpRequest download(ResultCallback callback) {
            OkHttpRequest request = new OkHttpDownloadRequest(url, tag, params, headers, destFileName, destFileDir);
            request.invokeAsyn(callback);
            return request;
        }

        /**
         * 同步下载
         * @return
         * @throws IOException
         */
        public String download() throws IOException {
            OkHttpRequest request = new OkHttpDownloadRequest(url, tag, params, headers, destFileName, destFileDir);
            return request.invoke(String.class);
        }

        /**
         * 异步显示图片
         * @param callback
         */
        public void displayImage(ResultCallback callback) {
            OkHttpRequest request = new OkHttpDisplayImgRequest(url, tag, params, headers, imageView, errorResId);
            request.invokeAsyn(callback);
        }


    }


}
